<template>
  <div class="goodsadd_container">
    <!-- <h3>这是添加商品页面</h3> -->
    <!-- 面包屑导航 -->
    <el-breadcrumb separator-class="el-icon-arrow-right">
      <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>商品管理</el-breadcrumb-item>
      <el-breadcrumb-item>添加商品</el-breadcrumb-item>
    </el-breadcrumb>
    <!-- 卡片视图 -->
    <el-card class="box-card">
      <!-- Alert 警告条 -->
      <el-alert title="添加商品信息" type="info" :closable="false" show-icon center> </el-alert>
      <!-- 步骤条 active属性默认值为0(值为number类型)，即停留在第一个步骤上 -->
      <el-steps :active="activeIndex" align-center>
        <el-step title="基本信息"></el-step>
        <el-step title="商品参数"></el-step>
        <el-step title="商品属性"></el-step>
        <el-step title="商品图片"></el-step>
        <el-step title="商品内容"></el-step>
        <el-step title="完成"></el-step>
      </el-steps>
      <!-- tabs标签页 -->
      <!-- 注意：1. el-tabs的子项只能是el-tab-pane,在form表单嵌入el-tabs，只能用el-from包裹el-tabs。和form表单嵌套table表格一样，
      form在外层，里面用table
      注意2；表单域标签的位置 label-position属性来控制 -->
      <el-form :model="tabsRuleForm" :rules="tabsRules" ref="tabsRuleForm" label-width="100px" :label-position="'top'">
        <!-- actvieName为0，tabs标签页默认选中了第一项基本信息。功能：当基本信息对应的内容区中的级联选择框
      没有选中三级分类时，不允许切换tabs标签页，同时给出切换失败提示。用到el-tabs的before-leave属性；
      官网描述：属性值为切换标签之前的钩子，若返回 false 或者返回 Promise 且被 reject，则阻止切换-->
        <el-tabs :tab-position="'left'" v-model="activeName" @tab-click="tabClick" :before-leave="beforeTabLeave">
          <el-tab-pane label="基本信息" name="0">
            <el-form-item label="商品名称" prop="goods_name">
              <el-input v-model="tabsRuleForm.goods_name"></el-input>
            </el-form-item>
            <el-form-item label="商品价格" prop="goods_price">
              <el-input v-model="tabsRuleForm.goods_price"></el-input>
            </el-form-item>
            <!-- type等于number,可以限定输入框中输入数值，但是包括负值，还是需要去自定义校验规则 -->
            <el-form-item label="商品数量" prop="goods_number">
              <el-input v-model="tabsRuleForm.goods_number"></el-input>
            </el-form-item>
            <el-form-item label="商品重量" prop="goods_weight">
              <el-input v-model="tabsRuleForm.goods_weight"></el-input>
            </el-form-item>
            <!-- 商品分类 级联选择框，.props声明次级菜单的展开方式(expandTrigger)、options数据节点
              label、value、children对应的变量-->
            <el-form-item label="商品分类" prop="goods_cat" class="cat_layout">
              <el-cascader v-model="selectedKeys" :options="catList" :props="casInfo" @change="handleChange" clearable class="add_cascader"></el-cascader>
            </el-form-item>
          </el-tab-pane>
          <el-tab-pane label="商品参数" name="1">
            <!-- 渲染商品参数item项 -->
            <el-form-item v-for="item in manyTabData" :label="item.attr_name" :key="item.attr_id">
              <!-- 用到新组件 多选框组 checkbox-->
              <!-- v-model的属性值，盛放所有checkbox的选中项的label值(类比级联选择框),设置为item.attr_vals，初始渲染时肯定是所有项被选中了,
              待取消勾选某一项时，item.attr_vals就会少了被取消勾选的那一项。原因：是取消选中某个复选框，
              item.attr_vals数组（数组元素是label属性值）中就删除一个元素 -->
              <!-- {{ item.attr_vals }} -->
              <el-checkbox-group v-model="item.attr_vals">
                <el-checkbox v-for="(item1, i1) in item.attr_vals" :label="item1" :key="i1" border></el-checkbox>
              </el-checkbox-group>
            </el-form-item>
          </el-tab-pane>
          <el-tab-pane label="商品属性" name="2">
            <!-- attr_vals可以为空，因此不需要为el-form-item 来设置pro="attr_vals",用于校验规则 -->
            <el-form-item v-for="(item, i) in onlyTabData" :key="i" :label="item.attr_name">
              <!-- {{ item.attr_vals }} -->
              <el-input v-model="item.attr_vals"></el-input>
            </el-form-item>
          </el-tab-pane>
          <el-tab-pane label="商品图片" name="3">
            <!--1. list-type="picture" 表示上传图片的呈现方式是列表的形式 -->
            <!--2. 注意：action="url",表示提交图片post请求的路径。 url写成upload,则
            放置的是相对路径(相对当前路由)，这种方式是不行的。原因：这种相对的方式会叠加上当前路由地址，变成
            http://localhost:9999/#/goods/add/upload ，而数据接口中端口号是8888，要写成绝对路径的形式 -->
            <!-- 应该写成 action="http://127.0.0.1:8888/api/private/v1/upload"
            BUG1:没有设置header属性前，图片可以正常的呈现在列表中，却并没有提交到action给出的请求地址中，在
            Network工具中看到{data: null, meta: {msg: "无效token", status: 400}},提示：token无效。需要手动
            为上传组件el-upload设置请求头属性:headers绑定在headerObj:{Authorization: sessionStroage.getItem('token')}
            3. 上传成功后的钩子函数 在on-success属性中指定
             -->
            <el-upload :action="uploadURL" :on-preview="handlePreview" :on-remove="handleRemove" :file-list="fileList" list-type="picture" :headers="headerObj" :on-success="handleSuccess">
              <el-button size="small" type="primary">点击上传</el-button>
              <div slot="tip" class="el-upload__tip">
                只能上传jpg/png文件，且不超过500kb
              </div>
            </el-upload>
          </el-tab-pane>
          <el-tab-pane label="商品内容" name="4">
            <!-- 富文本编辑器 组件 vue-quill-editor -->
            <quill-editor v-model="tabsRuleForm.goods_introduce"></quill-editor>
            <!-- 添加商品tabs标签的最后一项了，内容填写完，可以提交添加商品的内容 -->
            <el-button type="primary" class="add_btn" @click="add">添加商品</el-button>
          </el-tab-pane>
        </el-tabs>
      </el-form>
    </el-card>
    <!-- 添加商品-商品图片中预览列表中，点击图片名称预览时弹出对话框 -->
    <!-- 对话框何时弹出呢？是handlePreview事件出发时弹出 -->
    <el-dialog title="图片预览" :visible.sync="imgDialogVisible" width="50%">
      <img :src="previewPath" alt="" />
    </el-dialog>
  </div>
</template>
<script>
// import _from 'lodash';
export default {
  data() {
    const goodsPrice = (rule, value, cb) => {
      console.log(value);
      const reg = /^0$|^0\.\d+$|^[1-9](\d+)*(\.\d+)*$/;
      console.log(this.tabsRuleForm);
      if (value === undefined || value === '') {
        if (this.tabsRuleForm.goods_price + '') {
          return cb();
        } else {
          return cb(new Error('商品价格不能为空'));
        }
      } else {
        if (reg.test(value)) {
          return cb();
        } else {
          return cb(new Error('请输入合法的商品价格'));
        }
      }
    };
    const goodsWeight = (rule, value, cb) => {
      const reg = /^0$|^0\.\d+$|^[1-9](\d+)*(\.\d+)*$/;
      if (value === undefined || value === '') {
        if (this.tabsRuleForm.goods_weight + '') {
          return cb();
        } else {
          return cb(new Error('商品重量不能为空'));
        }
      } else {
        if (reg.test(value)) {
          return cb();
        } else {
          return cb(new Error('请输入合法的商品重量'));
        }
      }
    };
    const goodsNumber = (rule, value, cb) => {
      // 正整数包括0的正则表达式
      const reg = /^[+]{0,1}\d+$/;
      if (value === undefined || value === '') {
        if (this.tabsRuleForm.goods_number + '') return cb();
        else {
          return cb(new Error('商品数量不能为空'));
        }
      } else {
        if (reg.test(value)) return cb();
        else {
          return cb(new Error('请输入合法的商品数量'));
        }
      }
    };
    return {
      // 步骤条激活的选项索引，它默认从0开始
      activeIndex: 0,
      // 激活的标签页的即时值，绑定在v-model中
      activeName: '',
      // form表单数据对象
      tabsRuleForm: {
        goods_name: '',
        goods_cat: '',
        goods_price: '',
        goods_number: '',
        goods_weight: '',
        // 商品的详情描述
        goods_introduce: '',
        // 上传图片的临时路径
        pics: [],
        // 动态参数和静态属性组成的数组，它就是manyTabData和onlyTabData中的内容遍历后重新组成的数组
        attrs: []
      },
      // 表单验证规则
      tabsRules: {
        goods_name: [
          { required: true, message: '请输入商品名称', trigger: 'blur' },
          { min: 1, max: 15, message: '长度在 1 到 15 个字符', trigger: 'blur' }
        ],
        goods_price: [{ required: true, validator: goodsPrice, trigger: 'blur' }],
        goods_number: [{ required: true, validator: goodsNumber, trigger: 'blur' }],
        goods_weight: [{ required: true, validator: goodsWeight, trigger: 'blur' }],
        goods_cat: [{ required: true, message: '请选择商品分类', trigger: 'blur' }]
      },
      // 级联选择框catList商品分类列表
      catList: [],
      casInfo: {
        expandTrigger: 'hover',
        label: 'cat_name',
        value: 'cat_id',
        children: 'children'
      },
      // 商品各级分类id值组成的数组，空数组表示默认项【请选择】
      selectedKeys: [],
      // 添加商品>商品参数tab项内容区数组
      manyTabData: [],
      // 多选框组选中项label名称构成的集合数组
      // checkList: []
      // 添加商品>s商品属性 tab项内容区数组
      onlyTabData: [],
      // 存放的是上传图片提交的url地址
      uploadURL: 'http://127.0.0.1:8888/api/private/v1/upload',
      // 存放图片信息
      fileList: [],
      // Upload上传组件-请求头
      headerObj: {
        Authorization: sessionStorage.getItem('token')
      },
      // 点击图片文件名称，图片预览事件处理函数中的打印出url路径
      previewPath: '',
      // 商品图片标签页中，点击图片名称-弹出预览对话框的状态码
      imgDialogVisible: false
    };
  },
  computed: {
    thirdCatId() {
      // selectedKeys第三个元素，索引为2
      return this.selectedKeys[2];
    }
  },
  created() {
    this.getGoodsCatList();
  },
  // 让自定义的校验规则在mounted阶段调用，避免页面初次刷新时，页面还没有渲染完成，表单里面的数据还没有填充上，导致自定义规则校验失败

  methods: {
    // 自定义校验规则函数，让其在mounted阶段调用
    // tab标签页的点击事件
    // 实现tabs标签页和步骤条的联动，和leave-before属性都是通过值确定当前选项或刚离开的选项
    async tabClick() {
      // activeName从0开始取值
      console.log(this.activeName);
      console.log(typeof this.activeName); // string
      // 关联步骤条和tab切换页
      this.activeIndex = parseInt(this.activeName);
      // 可根据activeName的即时值，确定当前选中的是哪个面板
      if (this.activeName === '1') {
        // 说明标签页当前选中“商品参数”面板
        // 发起get请求，由于在商品信息中要求必选选中三级分类id才能切换，这里的id也是三级分类id,
        // 可以直接从his.selectedKeys数组中拿，为数组中第三个元素。当然，也可以添加一个
        // 三级分类的计算属性thirdCatId
        const { data: res } = await this.$http.get(`categories/${this.thirdCatId}/attributes`, {
          params: { sel: 'many' }
        });
        console.log(res.data);
        if (res.meta.status !== 200) {
          return this.$message.error('获取商品分类参数列表失败!');
        }
        // 获取商品分类参数成功
        // 和分类参数中一样，为了避免取消空checkbox选项，需要对res.data数组进行处理
        // 1. 由于''.split(' ')=['']，要判断res.data数组里的item.attr_vals是否为空？
        // 2. item.attr_vals不为空，该属性从字符串转换成数组
        res.data.forEach(item => {
          item.attr_vals = item.attr_vals !== '' ? item.attr_vals.split(' ') : [];
        });
        this.manyTabData = res.data;
        // this.activeName === '2'，则表示切换到了 “商品属性”面板，加载静态属性参数数据
        // 静态属性：sel:'only'
      } else if (this.activeName === '2') {
        const { data: res } = await this.$http.get(`categories/${this.thirdCatId}/attributes`, {
          params: { sel: 'only' }
        });
        console.log(res.data);
        if (res.meta.status !== 200) {
          return this.$message.error('静态属性参数列表获取失败');
        }
        this.onlyTabData = res.data;
      }
    },
    // 添加商品页面中，级联选择框，需要获取所有级别分类列表
    async getGoodsCatList() {
      const { data: res } = await this.$http.get('categories');
      console.log(res);
      if (res.meta.status !== 200) {
        return this.$message.error('获取商品分类数据失败');
      }
      // catList商品分类列表
      this.catList = res.data;
    },
    // 级联选择框切换时的事件处理函数
    handleChange() {
      console.log(this.selectedKeys);
      if (this.selectedKeys.length !== 3) {
        this.selectedKeys = [];
        return;
      }
      // 此情况下，选中了三级分类，则将三级分类数组selectedKeys以','拼接成字符串
      // 这也是数据接口参数的类型要求。注：join()方法不会改变原数组，缺省状态下，默认会以逗号(,)为分隔符
      this.tabsRuleForm.goods_cat = this.selectedKeys.join(',');
    },
    /*
      actvieName为0，tabs标签页默认选中了第一项基本信息。功能：当基本信息对应的内容区中的级联选择框
      没有选中三级分类时，不允许切换tabs标签页，同时给出切换失败提示。用到el-tabs的before-leave属性；
      官网描述：属性值为切换标签之前的钩子，若返回 false 或者返回 Promise 且被 reject，则阻止切换
    */
    beforeTabLeave(activeName, oldActiveName) {
      console.log('即将进入的标签名字' + activeName);
      console.log('即将离开的标签名字' + oldActiveName);
      // return false;
      // 不能切换的条件是：标签页处于第一个基本信息。切换时，oldActiveName为0。而且，级联选择框中选中的不是三级分类
      if (oldActiveName === '0' && this.selectedKeys.length !== 3) {
        // 提示不能切换
        this.$message.error('请选择一个三级商品分类才能切换');
        return false;
      }
    },
    // 上传图片预览的事件处理函数
    handlePreview(file) {
      console.log(file);
      // 设置预览对话框中的图片绝对路径；注：response。data.url拿到的是绝对路径
      this.previewPath = file.response.data.url;
      // 弹出预览对话框
      this.imgDialogVisible = true;
    },
    // 上传图片移除的事件处理函数
    handleRemove(file, fileList) {
      console.log(file);

      // 移除图片后：
      // 1.从file对象中拿到当前被移除图片的路径
      const removeImgPath = file.response.data.tmp_path;
      // console.log(removeImgPath);
      // 2.查找该路径在 this.tabsRuleForm.pics中的索引值
      // findIndex返回的是满足测试函数条件的第一个元素的索引值
      // 注意：removeImgPath是上传图片成功时，生成的临时路径，它是唯一的。即使两张图片完全
      // 相同，所生成的临时路径字符也完全不相同。因此，findIndex可以找到那个唯一的索引
      const index = this.tabsRuleForm.pics.findIndex(ele => {
        return ele.pic === removeImgPath;
      });
      // console.log(index);
      // 3.使用splice方法删除移除的元素，splice('被移除图片索引',1)
      this.tabsRuleForm.pics.splice(index, 1);
    },
    // 图片上传成功后的钩子函数
    handleSuccess(response, file, fileList) {
      console.log(response);
      if (response.meta.status !== 200) {
        return this.$message.error('上传图片失败');
      }
      // 上传成功
      this.tabsRuleForm.pics.push({ pic: response.data.tmp_path });
      console.log(this.tabsRuleForm); // 测试“添加商品”请求参数的变化
    },
    // 标签页商品内容中-添加商品按钮点击事件
    /* 注意：tabs标签页中商品内容-添加商品 按钮点击之前，需要将所有标签页逐个点击一遍。
    因为，商品参数和商品属性标签页中都是点击标签页，拿到manyTabData和onlyTabData
    数据源的。
     */
    add() {
      console.log(this.tabsRuleForm);
      // 表单校验
      this.$refs.tabsRuleForm.validate(async valid => {
        if (!valid) return this.$message.error('请先填写必要的表单项');
        // 校验成功-开始添加商品数据请求
        /* 注意1：添加商品API中，goods_cat goods_cat是以','分割的分类列表。
        如果在级联选择器中不设置selectedKeys,也可以使用tabsRuleForm.good_cat代替，但是有个问题
        this.tabsRuleForm.goods_cat=this.tabsRuleForm.goods_cat.join(',')后，goods_cat又数组变成了字符串，
        当代码执行到此处时，goods_cat变成字符串，级联选择器中v-model会监听到这种改变，但是级联选择器中
        tabsRuleForm.goods_cat又必须为数组，这样就出现了矛盾。当点击【添加商品】按钮时，代码报错
        因此，必须使用lodash包来深拷贝一个tabsRuleForm对象
        a.图形工具中安装运行依赖lodash，在本组件的script标签中引入_, _.cloneDeep()
        tabsRuleForm，级联选择器中仍然用数组。而拷贝项中goods_cat join()成字符串
        const form=_.cloneDeep(tabsRuleForm);
        form.goods_cat=form.goods_cat.join(',');
        传递参数时，
        tabsRuleForm:{
          goods_cat:form
        } */
        // 注意2：处理动态参数和静态属性 展开运算符,可以合并成数组，但是attrs参数中只需要attr_id和attr_value两个键，[{attr_id:'',attr_value:''}]，还是需要遍历
        // this.tabsRuleForm.attrs = [...this.manyTabData, ...this.onlyTabData];
        // console.log(this.manyTabData);
        // console.log(this.onlyTabData);
        this.manyTabData.forEach(item => {
          const paramInfo = {
            attr_id: item.attr_id,
            // 在商品参数中，渲染checkbox时，将attr_vals转化成了数组。此时，作为参数应该还原成字符串，用空格进行分割
            attr_value: item.attr_vals.join(' ')
          };
          this.tabsRuleForm.attrs.push(paramInfo);
        });
        this.onlyTabData.forEach(item => {
          const attrInfo = {
            attr_id: item.attr_id,
            // 在商品属性中，渲染el-input内容时，attr_vals就是空格组成的字符串，不需要join方法转换
            attr_value: item.attr_vals
          };
          this.tabsRuleForm.attrs.push(attrInfo);
        });
        console.log(this.tabsRuleForm.attrs);
        // 发起添加商品的post请求
        const { data: res } = await this.$http.post('goods', this.tabsRuleForm);
        if (res.meta.status !== 201) {
          return this.$message.error('提交添加商品请求失败!');
        }
        console.log(res.data);
        // 1.添加成功后
        // 1.1 给出提示
        this.$message.success('添加商品成功');
        // 1.2 编程式导航跳转至商品列表页 /goods
        this.$router.push('/goods');
      });
    }
  }
};
</script>
<style lang="less" scoped>
.goodsadd_container {
  .el-tabs__content {
    .el-tab-pane {
      line-height: 30px;
    }
  }
  /* 表单标签左对齐，默认居中了，在全局样式文件设置 */
  /* 商品分类中标签和级联选择框的位置在同一行，原因是前面使用文本输入框时，el-input width为100%，就被挤到了下一行。而级联选择框宽度没有设置100%*/
  .cat_layout {
    > .el-form-item__content {
      > .el-cascader {
        display: block;
        width: 300px;
      }
    }
  }
  /* 复选框的外边距 */
  .el-form-item {
    display: flex;
    flex-direction: column;
    .el-form-item__content {
      top: 20px !important;
      .el-checkbox-group {
        text-align: left;
        .el-checkbox {
          margin: 0 10px 10px 0 !important;
        }
      }
    }
  }
  /* 商品图片文本居中样式 注意；交集选择器.,一般标签选择器放在前面，否则交集选择器可能无效*/
  div[id='pane-3'].el-tab-pane,
  div[id='pane-4'].el-tab-pane {
    text-align: left !important;
  }
  /* 富文本编辑器 编辑区高度，置于全局样式中生效 */
  /* 为商品内容标签页对应内容区按钮添加样式 */
  .add_btn {
    margin-top: 15px;
  }
}
/* 预览图片样式设置 */
.el-dialog__body {
  img {
    width: 100%;
    vertical-align: bottom;
    margin-bottom: 20px;
  }
}
</style>
